<?php

namespace common\modules\user\models;

use common\components\ActiveRecord;
use common\modules\user\helpers\Security;
use common\modules\user\Module;
use common\modules\user\traits\ModuleTrait;
use yii\base\NotSupportedException;
use yii\behaviors\TimestampBehavior;
use yii\helpers\ArrayHelper;
use yii\web\IdentityInterface;
use Yii;

/**
 * Class User
 *
 * @package common\modules\user\models
 * @property integer $id            ID
 * @property string  $username      Username
 * @property string  $email         E-mail
 * @property string  $password_hash Password hash
 * @property string  $auth_key      Authentication key
 * @property string  $role          Role
 * @property integer $status_id     Status
 * @property integer $join_type     用户加入来源
 * @property integer $group_id      用户组ID
 * @property Group   $group         用户组实例
 * @property integer $created_at    Created time
 * @property integer $updated_at    Updated time
 * @property Profile $profile       Profile
 */
class User extends ActiveRecord implements IdentityInterface
{
    use ModuleTrait;

    /**
     * @const int 系统自建用户
     */
    const JOIN_TYPE_INTERNAL = 0;

    /**
     * @const int UCenter转移过来的用户
     */
    const JOIN_TYPE_UCENTER = 1;

    /** Inactive status */
    const STATUS_INACTIVE = 0;
    /** Active status */
    const STATUS_ACTIVE = 1;
    /** Banned status */
    const STATUS_BANNED = 2;
    /** Deleted status */
    const STATUS_DELETED = 3;

    /**
     * Default role
     */
    const ROLE_DEFAULT = 'user';

    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return '{{%user_user}}';
    }

    /**
     * @inheritdoc
     */
    public static function findIdentity($id)
    {
        return static::findOne($id);
    }

    /**
     * @inheritdoc
     */
    public static function findIdentityByAccessToken($token, $type = null)
    {
        throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
    }

    /**
     * Find users by IDs.
     *
     * @param mixed $ids   IDs
     * @param null  $scope Scope
     *
     * @return array|\yii\db\ActiveRecord[] Users
     */
    public static function findIdentities($ids, $scope = null)
    {
        $query = static::find()->where(['id' => $ids]);
        if ($scope !== null)
        {
            if (is_array($scope))
            {
                foreach ($scope as $value)
                {
                    $query->$value();
                }
            }
            else
            {
                $query->$scope();
            }
        }
        return $query->all();
    }

    /**
     * @inheritdoc
     */
    public static function find()
    {
        return new UserQuery(get_called_class());
    }

    /**
     * Find model by username.
     *
     * @param string $username Username
     * @param string $scope    Scope
     *
     * @return array|\yii\db\ActiveRecord[] User
     */
    public static function findByUsername($username, $scope = null)
    {
        $query = static::find()->where(['username' => $username]);
        if ($scope !== null)
        {
            if (is_array($scope))
            {
                foreach ($scope as $value)
                {
                    $query->$value();
                }
            }
            else
            {
                $query->$scope();
            }
        }
        return $query->one();
    }

    /**
     * Find model by email.
     *
     * @param string $email Email
     * @param string $scope Scope
     *
     * @return array|\yii\db\ActiveRecord[] User
     */
    public static function findByEmail($email, $scope = null)
    {
        $query = static::find()->where(['email' => $email]);
        if ($scope !== null)
        {
            if (is_array($scope))
            {
                foreach ($scope as $value)
                {
                    $query->$value();
                }
            }
            else
            {
                $query->$scope();
            }
        }
        return $query->one();
    }

    /**
     * Find model by token.
     *
     * @param string $token Token
     * @param string $scope Scope
     *
     * @return array|\yii\db\ActiveRecord[] User
     */
    public static function findByToken($token, $scope = null)
    {
        $query = static::find()->where(['token' => $token]);
        if ($scope !== null)
        {
            if (is_array($scope))
            {
                foreach ($scope as $value)
                {
                    $query->$value();
                }
            }
            else
            {
                $query->$scope();
            }
        }
        return $query->one();
    }

    /**
     * @inheritdoc
     */
    public function behaviors()
    {
        return [
            'timestampBehavior' => [
                'class' => TimestampBehavior::className(),
            ],
        ];
    }

    /**
     * @inheritdoc
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @inheritdoc
     */
    public function getAuthKey()
    {
        return $this->auth_key;
    }

    /**
     * Auth Key validation.
     *
     * @param string $authKey
     *
     * @return boolean
     */
    public function validateAuthKey($authKey)
    {
        return $this->auth_key === $authKey;
    }

    /**
     * Password validation.
     *
     * @param string $password
     *
     * @return boolean
     */
    public function validatePassword($password)
    {
        return Yii::$app->security->validatePassword($password, $this->password_hash);
    }

    /**
     * @inheritdoc
     */
    public function transactions()
    {
        return [
            self::SCENARIO_DEFAULT => self::OP_ALL,
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'username'   => Module::t('user', 'ATTR_USERNAME'),
            'email'      => Module::t('user', 'ATTR_EMAIL'),
            'role'       => Module::t('user', 'ATTR_ROLE'),
            'group_id'   => Yii::t('app/user', 'Group ID'),
            'join_type'  => Yii::t('app/user', 'Join Type'),
            'status_id'  => Module::t('user', 'ATTR_STATUS'),
            'created_at' => Module::t('user', 'ATTR_CREATED'),
            'updated_at' => Module::t('user', 'ATTR_UPDATED'),
        ];
    }

    /**
     * @inheritdoc
     */
    public function beforeSave($insert)
    {
        if (parent::beforeSave($insert))
        {
            if ($this->isNewRecord)
            {
                // Set default status
                if ( ! $this->status_id)
                {
                    $this->status_id = $this->module->requireEmailConfirmation ? self::STATUS_INACTIVE : self::STATUS_ACTIVE;
                }
                // Set default role
                if ( ! $this->role)
                {
                    $this->role = self::ROLE_DEFAULT;
                }
                // Generate auth and secure keys
                $this->generateAuthKey();
                $this->generateToken();
            }
            return true;
        }
        return false;
    }

    /**
     * @inheritdoc
     */
    public function beforeDelete()
    {
        if (parent::beforeDelete())
        {
            Yii::$app->authManager->revokeAll($this->id);
            return true;
        }
        else
        {
            return false;
        }
    }

    /**
     * @return Profile|null User profile
     */
    public function getProfile()
    {
        return $this->hasOne(Profile::className(), ['user_id' => 'id'])->inverseOf('user');
    }

    /**
     * Generates "remember me" authentication key.
     */
    public function generateAuthKey()
    {
        $this->auth_key = Yii::$app->security->generateRandomString();
    }

    /**
     * Generates secure key.
     */
    public function generateToken()
    {
        $this->token = Security::generateExpiringRandomString();
    }

    /**
     * Activates user account.
     *
     * @return boolean true if account was successfully activated
     */
    public function activation()
    {
        $this->status_id = self::STATUS_ACTIVE;
        $this->generateToken();
        return $this->save(false);
    }

    /**
     * Recover password.
     *
     * @param string $password New Password
     *
     * @return boolean true if password was successfully recovered
     */
    public function recovery($password)
    {
        $this->setPassword($password);
        $this->generateToken();
        return $this->save(false);
    }

    /**
     * Generates password hash from password and sets it to the model.
     *
     * @param string $password
     */
    public function setPassword($password)
    {
        $this->password_hash = Yii::$app->security->generatePasswordHash($password);
    }

    /**
     * Change user password.
     *
     * @param string $password Password
     *
     * @return boolean true if password was successfully changed
     */
    public function password($password)
    {
        $this->setPassword($password);
        return $this->save(false);
    }

    /**
     * 返回当前用户关联的用户组
     *
     * @return \yii\db\ActiveQuery|Group
     */
    public function getGroup()
    {
        return $this->hasOne(Group::className(), [
            'id' => 'group_id'
        ]);
    }

    /**
     * @return array Status array.
     */
    public static function getStatusArray()
    {
        return [
            self::STATUS_ACTIVE   => Module::t('user', 'STATUS_ACTIVE'),
            self::STATUS_INACTIVE => Module::t('user', 'STATUS_INACTIVE'),
            self::STATUS_BANNED   => Module::t('user', 'STATUS_BANNED'),
        ];
    }

    /**
     * @return array
     */
    public static function getJoinTypeArray()
    {
        return [
            self::JOIN_TYPE_INTERNAL => Yii::t('app/user', 'JOIN_TYPE_INTERNAL'),
            self::JOIN_TYPE_UCENTER => Yii::t('app/user', 'JOIN_TYPE_UCENTER'),
        ];
    }

    /**
     * @return array Role array.
     */
    public static function getRoleArray()
    {
        return ArrayHelper::map(Yii::$app->authManager->getRoles(), 'name', 'description');
    }
}
